package model

import (
	"database/sql"
	"fmt"
	sq "github.com/Masterminds/squirrel"
	"strings"

	"github.com/zeromicro/go-zero/core/stores/builder"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlc"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"github.com/zeromicro/go-zero/core/stringx"
)

var (
	countyFieldNames          = builder.RawFieldNames(&County{}, true)
	countyRows                = strings.Join(countyFieldNames, ",")
	countyRowsExpectAutoSet   = strings.Join(stringx.Remove(countyFieldNames, "create_time", "update_time"), ",")
	countyRowsWithPlaceHolder = builder.PostgreSqlJoin(stringx.Remove(countyFieldNames, "id", "create_time", "update_time"))

	cachePublicCountyIdPrefix   = "cache:public:county:id:"
	cachePublicCountyCodePrefix = "cache:public:county:code:"
)

type (
	CountyModel interface {
		Insert(data *County) (sql.Result, error)
		FindOne(id int64) (*County, error)
		Update(data *County) error
		Delete(id int64) error
		countyCustomModel
	}
	countyCustomModel interface {
		FindOneByCode(code string) (*County, error)
		FindListByCityCode(code string) ([]County, error)
	}
	defaultCountyModel struct {
		sqlc.CachedConn
		table string
	}

	County struct {
		Id       int64  `db:"id"`
		Name     string `db:"name"`
		CityId   string `db:"city_id"`
		CountyId string `db:"county_id"`
	}
)

func NewCountyModel(conn sqlx.SqlConn, c cache.CacheConf) CountyModel {
	return &defaultCountyModel{
		CachedConn: sqlc.NewConn(conn, c),
		table:      `"public"."county"`,
	}
}

func (m *defaultCountyModel) Insert(data *County) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values ($1, $2, $3, $4)", m.table, countyRowsExpectAutoSet)
	ret, err := m.ExecNoCache(query, data.Id, data.Name, data.CityId, data.CountyId)

	return ret, err
}

func (m *defaultCountyModel) FindOne(id int64) (*County, error) {
	publicCountyIdKey := fmt.Sprintf("%s%v", cachePublicCountyIdPrefix, id)
	var resp County
	err := m.QueryRow(&resp, publicCountyIdKey, func(conn sqlx.SqlConn, v interface{}) error {
		query := fmt.Sprintf("select %s from %s where id = $1 limit 1", countyRows, m.table)
		return conn.QueryRow(v, query, id)
	})
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}
func (m *defaultCountyModel) FindOneByCode(code string) (*County, error) {
	publicCountyCodeKey := fmt.Sprintf("%s%v", cachePublicCountyCodePrefix, code)
	var resp County
	err := m.QueryRow(&resp, publicCountyCodeKey, func(conn sqlx.SqlConn, v interface{}) error {
		query := fmt.Sprintf("select %s from %s where county_id = $1 limit 1", countyRows, m.table)
		return conn.QueryRow(v, query, code)
	})
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultCountyModel) FindListByCityCode(code string) ([]County, error) {
	var resp []County
	query, args, err := sq.Select(cityRows).From(m.table).Where(sq.Eq{"city_id": code}).PlaceholderFormat(sq.Dollar).ToSql()
	if err != nil {
		return nil, err
	}
	err = m.QueryRowsNoCache(&resp, query, args...)
	if err != nil {
		return nil, err
	}
	return resp, nil
}
func (m *defaultCountyModel) Update(data *County) error {
	publicCountyIdKey := fmt.Sprintf("%s%v", cachePublicCountyIdPrefix, data.Id)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set %s where id = $1", m.table, countyRowsWithPlaceHolder)
		return conn.Exec(query, data.Id, data.Name, data.CityId, data.CountyId)
	}, publicCountyIdKey)
	return err
}

func (m *defaultCountyModel) Delete(id int64) error {

	publicCountyIdKey := fmt.Sprintf("%s%v", cachePublicCountyIdPrefix, id)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("delete from %s where id = $1", m.table)
		return conn.Exec(query, id)
	}, publicCountyIdKey)
	return err
}

func (m *defaultCountyModel) formatPrimary(primary interface{}) string {
	return fmt.Sprintf("%s%v", cachePublicCountyIdPrefix, primary)
}

func (m *defaultCountyModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
	query := fmt.Sprintf("select %s from %s where id = $1 limit 1", countyRows, m.table)
	return conn.QueryRow(v, query, primary)
}
